package com.tianji.learning.service.impl;

import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BooleanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mq.message.SIgnInMessage;
import com.tianji.learning.service.ISignRecordService;
import lombok.RequiredArgsConstructor;
import org.checkerframework.checker.units.qual.N;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.List;

@Service
@RequiredArgsConstructor
public class SignRecordServiceImpl implements ISignRecordService {

    private final StringRedisTemplate redisTemplate;
    private final RabbitMqHelper mqHelper;

    @Override
    public Byte[] querySignRecords() {
        //1.获取登录用户
        Long userId = UserContext.getUser();
        //2.获取日期
        LocalDate now = LocalDate.now();
        int dayOfMonth = now.getDayOfMonth();
        //3.拼接key
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX
                + userId
                +now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);
        //4.读取
        List<Long> result = redisTemplate.opsForValue()
                .bitField(key,BitFieldSubCommands.create().get(
                        BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        if (CollUtils.isEmpty(result)){
            return new Byte[0];
        }
        int num = result.get(0).intValue();

        Byte[] arr = new Byte[dayOfMonth];
        int pos = dayOfMonth -1;
        while (pos >= 0){
            arr[pos--] = (byte)(num&1);
            // 把数字右移一位，抛弃最后一个bit位，继续下一个bit位
            num >>>= 1;
        }
        return arr;
    }

    @Override
    public SignResultVO addSignRecords() {
        //1.签到
        //1.1获取登录用户
        Long userId = UserContext.getUser();
        //1.2获取日期
        LocalDate now = LocalDate.now();
        //1.3拼接key
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX
                + userId
                +now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);
        //1.4计算offset
        int offset = now.getDayOfMonth() - 1;
        //1.5保存签到信息
        Boolean exist = redisTemplate.opsForValue().setBit(key, offset, true);
        if (BooleanUtils.isTrue(exist)) {
            throw new BizIllegalException("您今天已经签到过了");
        }
        //2.计算连续签到的天数
        int signDays = countSignDays(key,now.getDayOfMonth());
        //3.计算签到得分
        int rwardsPoints = 0;
        switch (signDays){
            case 7:
                rwardsPoints = 10;
                break;
            case 14:
                rwardsPoints = 20;
                break;
            case 28:
                rwardsPoints = 40;
                break;
        }
        //4.保存积分明细记录
        mqHelper.send(
                MqConstants.Exchange.LEARNING_EXCHANGE,
                MqConstants.Key.SIGN_IN,
                new SIgnInMessage(userId, rwardsPoints + 1)
        );
        //5.封装返回
        SignResultVO vo = new SignResultVO();
        vo.setSignDays(signDays);
        vo.setSignPoints(rwardsPoints);
        return vo;
    }

    private int countSignDays(String key, int  len) {
        //1.获取本月从第一天开始，到今天为止的所有签到记录
        List<Long> result = redisTemplate.opsForValue()
                .bitField(key, BitFieldSubCommands.create().get(
                        BitFieldSubCommands.BitFieldType.unsigned(len)).valueAt(0));
        if (CollUtils.isEmpty(result)){
            return 0;
        }
        int num = result.get(0).intValue();
        //2，定义一个计数器
        int count = 0;
        //3.遍历，与1做与运算，得到最后一个bit，判断是否为0，如果为0，则结束循环，否则计数器加1
        while ((num & 1) == 1){
            count++;
            num >>>= 1;
        }
        return count;
    }
}

















